/**************************************************************************************************
 * red library                                                                                    *
 * Copyright © 2012 David Kretzmer                                                                *
 *                                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software  *
 * and associated documentation files (the "Software"), to deal in the Software without           *
 * restriction,including without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute,sublicense, and/or sell copies of the Software, and to permit persons to whom the   *
 * Software is furnished to do so, subject to the following conditions:                           *
 *                                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies or       *
 * substantial portions of the Software.                                                          *
 *                                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  *
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     *
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        *
 *                                                                                                *
 *************************************************************************************************/

#pragma once

#include "red/static_vector_base.hpp"

#include <vector>


namespace red
{

	//=============================================================================================
	// Vector with static size
	//=============================================================================================
	template<typename Real, size_t Dimension>
	class static_vector : public static_vector_base<static_vector<Real, Dimension>, Real, Dimension>
	{
	public:
        static size_t const dimension = Dimension;

        typedef static_vector_base<static_vector<Real, dimension>, Real, dimension> base_type;
		typedef Real real_type;

		static bool const is_static = true;

		static_vector() = default;

		static_vector(static_vector const &rhs) :
			base_type(rhs)
		{

		}

		real_type const& element_at(size_t index) const
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type& element_at(size_t index)
		{
            assert(index < dimension);
			return m_components[index];
		}

        void resize(size_t size) { assert(size == dimension); }

	private:
        real_type m_components[dimension];
	};


	//=============================================================================================
	// 2D-Vector
	//=============================================================================================
	template<typename Real>
	class static_vector<Real, 2> : public static_vector_base<static_vector<Real, 2>, Real, 2>
	{
	public:
        static size_t const dimension = 2;

        typedef static_vector_base<static_vector<Real, dimension>, Real, dimension> base_type;
		typedef Real real_type;

		static bool const is_static = true;

		static_vector() = default;

		static_vector(real_type x, real_type y)
		{
			element_at(0) = x;
			element_at(1) = y;
		}

		static_vector(static_vector const &rhs) :
			base_type(rhs)
		{

		}

		real_type const& element_at(size_t index) const
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type& element_at(size_t index)
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type x() const { return element_at(0); }
		real_type& x() { return element_at(0); }

		real_type y() const { return element_at(1); }
		real_type& y() { return element_at(1); }

        void resize(size_t size) { assert(size == dimension); }

	private:
        real_type m_components[dimension];
	};


	//=============================================================================================
	// 3D-Vector
	//=============================================================================================
	template<typename Real>
	class static_vector<Real, 3> : public static_vector_base<static_vector<Real, 3>, Real, 3>
	{
	public:
        static size_t const dimension = 3;

        typedef static_vector_base<static_vector<Real, dimension>, Real, dimension> base_type;
		typedef Real real_type;

		static bool const is_static = true;

		static_vector() = default;

		static_vector(real_type x, real_type y, real_type z)
		{
			this->x() = x;
			this->y() = y;
			this->z() = z;
		}

		static_vector(static_vector const &rhs) :
			base_type(rhs)
		{

		}

		real_type const& element_at(size_t index) const
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type& element_at(size_t index)
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type x() const { return element_at(0); }
		real_type& x() { return element_at(0); }

		real_type y() const { return element_at(1); }
		real_type& y() { return element_at(1); }

		real_type z() const { return element_at(2); }
		real_type& z() { return element_at(2); }

        void resize(size_t size) { assert(size == dimension); }

	private:
        real_type m_components[dimension];
	};


	//=============================================================================================
	// 4D-Vector
	//=============================================================================================
	template<typename Real>
	class static_vector<Real, 4> : public static_vector_base<static_vector<Real, 4>, Real, 4>
	{
	public:
        static size_t const dimension = 4;

        typedef static_vector_base<static_vector<Real, dimension>, Real, dimension> base_type;
		typedef Real real_type;

		static bool const is_static = true;

		static_vector() = default;

		static_vector(static_vector const &rhs) :
			base_type(rhs)
		{

		}

		real_type  const& element_at(size_t index) const
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type& element_at(size_t index)
		{
            assert(index < dimension);
			return m_components[index];
		}

		real_type x() const { return element_at(0); }
		real_type& x() { return element_at(0); }

		real_type y() const { return element_at(1); }
		real_type& y() { return element_at(1); }

		real_type z() const { return element_at(2); }
		real_type& z() { return element_at(2); }

		real_type w() const { return element_at(3); }
		real_type& w() { return element_at(3); }

        void resize(size_t size) { assert(size == dimension); }

		using base_type::operator =;

	private:
        real_type m_components[dimension];
	};


	//=============================================================================================
	// Make it a valid vector
	//=============================================================================================
	template<typename Real, size_t Dimension>
	struct is_vector<static_vector<Real, Dimension> >
	{
		static bool const value = true;
	};


	//=============================================================================================
	// Typedefs
	//=============================================================================================
	typedef static_vector<int4, 2> vector2i;
	typedef static_vector<float4, 2> vector2f;
	typedef static_vector<float8, 2> vector2d;

	typedef static_vector<int4, 3> vector3i;
	typedef static_vector<float4, 3> vector3f;
	typedef static_vector<float8, 3> vector3d;

	typedef static_vector<int4, 4> vector4i;
	typedef static_vector<float4, 4> vector4f;
	typedef static_vector<float8, 4> vector4d;

} // namespace: red
